package com.rgbgame.game;

import processing.core.PApplet;
import com.rgbgame.util.Util;

/**
 * A player object consists of:
 * 1) a set of Particles
 * 2) a set of interlocking Springs
 * 3) a com.rgbgame.game.Pose object
 *
 *
 * Should consist of:
 * 1) player metadata (name, id, location, rotation, etc)
 * 2) an array of Poses
 * 3)
 * @author Will
 * @date Mar 4, 2008 - 1:51:34 PM
 */
public class Player {

    rgbGame game;

    // With tstep .015, stepsPerFrame 3, springConstant ~8100 is the "edge";
    // 7000 is stable, in between we get "quantum" uncertainty (half-blowups)
    // tstep .015, stepsPerFrame 10

    /** strength of player's springs **/
    public float springConstant = 1900.0f;


    private Spring springList[];
    protected Particle particle[];
    protected int numParticles;
    private Pose pose;
    private float springLengthEasing;
    private float springStrength;
    private float springDamping;
    public boolean drawn;
    public boolean simulating;

    {
        springLengthEasing = 60; // speed of easing
        springStrength = springConstant;
        springDamping = 24.9f;
        drawn = true;
        simulating = true;
    }

    public void setSpringStrength(float s) {
        springStrength = s;
        for (int i = 0; i < springList.length; i++) {
            springList[i].setForceConstant(s);
        }
    }

    public Player(rgbGame g, Particle[] myParts) {
        game = g;
        particle = new Particle[myParts.length];
        for (int i = 0; i < myParts.length; i++) {
            particle[i] = new Particle(myParts[i]);

            particle[i].setPlayer(true);
        }
        numParticles = myParts.length;
        springList = new Spring[numParticles * (numParticles - 1) / 2];
        pose = new Pose(Util.particlesToVectors(myParts));
        initSpringLengths();
    }

    public void draw() {
        if (!drawn)
            return;
        for (int i = 0; i < numParticles; i++) {
            particle[i].draw();
        }
    }

    public void draw(float myAlpha) {
        if (!drawn)
            return;
        for (int i = 0; i < numParticles; i++) {
            particle[i].draw(myAlpha);
        }
    }

    private void handleKeys() {
    }

    public void setPose(Pose _pose) {
        pose = _pose;
    }

    public Pose getPose() {
        return pose;
    }

    /**
     * translate the player's position by a vector.
     * @param moveMe translation vector
     */
    public void translate(Vector3d moveMe) {
        for (int i = 0; i < numParticles; i++) {
            particle[i].position.add(moveMe);
            particle[i].positionLast.add(moveMe);
        }
    }

    /**
     * rotates the player by a constant amount.
     * @param rotBy rotation amount in radians
     */
    public void rotate(float rotBy) {
        float cosTheta = PApplet.cos(rotBy);
        float sinTheta = PApplet.sin(rotBy);
        for (int i = 0; i < numParticles; i++) {
            Vector3d pos = particle[i].position;
            particle[i].position = new Vector3d(pos.x * cosTheta + pos.y
                    * sinTheta, pos.x * sinTheta - pos.y * cosTheta, 0.0f);
            particle[i].positionLast = new Vector3d(pos.x * cosTheta
                    + pos.y * sinTheta,
                    pos.x * sinTheta - pos.y * cosTheta, 0.0f);
        }
    }

    public void scale(float factor) {
        for (int i = 0; i < numParticles; i++) {
            particle[i].position = particle[i].position.times(factor);
            particle[i].positionLast = particle[i].positionLast
                    .times(factor);
        }
    }

    public void addForce(Vector3d fAdd, int index) {
        particle[index].forceAdd(fAdd);
    }

    public Particle getParticle(int index) {
        return particle[index];
    }

    public void timeStep(float tstep) {
        if (!simulating)
            return;
        handleKeys();
        easeSpringLengths(tstep);
        for (int i = 0; i < springList.length; i++) {
            springList[i].applyForce();
        }
        for (int i = 0; i < particle.length; i++) {
            particle[i].timeStep(tstep);
        }
    }

    /**
     * This method creates Springs to connect each com.rgbgame.game.Particle with every other com.rgbgame.game.Particle.
     */
    private void initSpringLengths() {
        for (int i = 0; i < numParticles - 1; i++) {
            for (int j = i + 1; j < numParticles; j++) {
                int k = getSpringIndex(i, j);
                springList[k] = new Spring(particle[i], particle[j],
                        pose.targetSpring[k], springStrength, springDamping);
            }
        }
    }

    private void easeSpringLengths(float tstep) {
        for (int i = 0; i < numParticles - 1; i++) {
            for (int j = i + 1; j < numParticles; j++) {
                int k = getSpringIndex(i, j);
                springList[k].setRestLength(springList[k].getRestLength()
                        * (1.0f - tstep * springLengthEasing)
                        + (tstep * springLengthEasing)
                        * pose.targetSpring[k]);
            }
        }
    }

    /**
     * Gives the index into the springList array, provided the two com.rgbgame.game.Particle indices.
     * @param _i first particle index
     * @param _j second particle index
     * @return index into the springList array.
     */
    private int getSpringIndex(int _i, int _j) {
        int result = 0;
        for (int i = 0; i < _i; i++) {
            result += numParticles - 2 - i;
        }
        result += _j - 1;
        return result;
    }

    public void constrainBox(Vector3d minV, Vector3d maxV) {
        for (int i = 0; i < numParticles; i++) {
            particle[i].constrainBox(minV, maxV);
        }
    }

    public void explodeAndKill() {
        game.myPlayer.drawn = false;
        game.myPlayer.simulating = false;
        for (int i = 0; i < game.myPlayer.particle.length; i++) {
            Particle particle = game.myPlayer.particle[i];
            game.burstFXParticles(100, particle.position, particle.position
                    .minus(particle.positionLast).times(1),
                    particle.myColor, 3.1f, 255.0f);
            particle.position.set(-10000f, -10000f, -10000f);
            particle.positionLast.set(-10000f, -10000f, -10000f);
            particle.truePositionLast.set(-10000f, -10000f, -10000f);
        }
        game.playerDied = true;
        game.playerDied();
    }
}
